﻿#ifndef __JQHttpServer_h__
#define __JQHttpServer_h__

#ifndef QT_NETWORK_LIB
#   error("Plwaer add network in pro file")
#endif

#ifndef QT_CONCURRENT_LIB
#   error("Plwaer add concurrent in pro file")
#endif

// C++ lib import
#include <functional>

// Qt lib import
#include <QObject>
#include <QSharedPointer>
#include <QPointer>
#include <QVector>
#include <QMap>
#include <QSet>
#include <QMutex>
#include <QHostAddress>

class QIODevice;
class QTcpServer;
class QLocalServer;
class QThreadPool;
class QHostAddress;
class QTimer;
class QImage;

class Session: public QObject
{
    Q_OBJECT

public:
    Session( const QPointer< QIODevice > &tcpSocket );

    ~Session();

    inline void setHandleAcceptedCallback(const std::function< void(const QPointer< Session > &) > &callback)
    { handleAcceptedCallback_ = callback; }

    inline QString requestMethodToken() const { return requestMethodToken_; }

    inline QString requestUrl() const { return requestUrl_; }

    inline QString requestCrlf() const { return requestCrlf_; }

    inline QMap< QString, QString > headersData() const { return headersData_; }

    inline QByteArray requestRawData() const { return requestRawData_; }

public slots:
    void replyText(const QString &replyData);

    void replyJsonObject(const QJsonObject &jsonObject);

    void replyJsonArray(const QJsonArray &jsonArray);

    void replyFile(const QString &filePath);

    void replyImage(const QImage &image);

private:
    void inspectionBufferSetup1();

    void inspectionBufferSetup2();

private:
    QPointer< QIODevice > ioDevice_;
    std::function< void(const QPointer< Session > &) > handleAcceptedCallback_;
    QSharedPointer< QTimer > timerForClose_;

    QByteArray buffer_;

    QString requestMethodToken_;
    QString requestUrl_;
    QString requestCrlf_;

    QMap< QString, QString > headersData_;
    bool headerAcceptedFinish_ = false;

    QByteArray requestRawData_;

    qint64 waitWrittenByteCount_ = 0;
    QSharedPointer< QIODevice > ioDeviceForReply_;
};

class AbstractManage: public QObject
{
    Q_OBJECT

public:
    AbstractManage(const int &handleMaxThreadCount);

    ~AbstractManage();

    inline void setHttpAcceptedCallback(const std::function< void(const QPointer< Session > &session) > &httpAcceptedCallback)
    { httpAcceptedCallback_ = httpAcceptedCallback; }

    virtual bool isRunning() = 0;

public slots:
    bool begin();

    void close();

protected:
    virtual bool onStart() = 0;

    virtual void onFinish() = 0;

    bool startServerThread();

    void stopHandleThread();

    void stopServerThread();

    void newSession(const QPointer< Session > &session);

    void handleAccepted(const QPointer< Session > &session);

signals:
    void readyToClose();

protected:
    QSharedPointer< QThreadPool > handleThreadPool_;
    QSharedPointer< QThreadPool > serverThreadPool_;

    QMutex mutex_;

    std::function< void(const QPointer< Session > &session) > httpAcceptedCallback_;

    QSet< Session * > availableSessions_;
};

class TcpServerManage: public AbstractManage
{
    Q_OBJECT

public:
    TcpServerManage(const int &handleMaxThreadCount = 2);

    ~TcpServerManage();

    bool listen(const QHostAddress &address, const quint16 &port);

private:
    bool isRunning();

    bool onStart();

    void onFinish();

private:
    QPointer< QTcpServer > tcpServer_;

    QHostAddress listenAddress_;
    quint16 listenPort_;
};

class LocalServerManage: public AbstractManage
{
    Q_OBJECT

public:
    LocalServerManage(const int &handleMaxThreadCount);

    ~LocalServerManage();

    bool listen(const QString &name);

private:
    bool isRunning();

    bool onStart();

    void onFinish();

private:
    QPointer< QLocalServer > localServer_;

    QString listenName_;
};

#endif//__JQHttpServer_h__
